﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Elderos.AI;

namespace AccordTrainer
{
    public class ParallelQualityComputer
    {
        private int _maxDegreeOfParallelism = Environment.ProcessorCount;
        public int MaxDegreeOfParallelism
        {
            get { return _maxDegreeOfParallelism; }
            set { _maxDegreeOfParallelism = value; }
        }

        public Quality GetBestQuality(TrainItem<ItemInfo>[] trainItems, ThresholdQualityComputer qualityComputer, double delta)
        {
            List<double>[] thresholds = new List<double>[MaxDegreeOfParallelism];

            for (int i = 0; i < thresholds.Length; i++)
            {
                thresholds[i] = new List<double>();
            }

            int k = 0;
            for (double d = qualityComputer.LeftThreshold; d <= qualityComputer.RightThreshold; d += delta)
            {
                thresholds[k % MaxDegreeOfParallelism].Add(d);
                k++;
            }

            var tasks = new Task<Quality>[thresholds.Length];
            for (int i = 0; i < thresholds.Length; i++)
            {
                int j = i;
                tasks[j] = new Task<Quality>(() => ProcessPart(trainItems, qualityComputer, thresholds[j]));
                tasks[j].Start();
            }

            Task.WaitAll(tasks);


            return tasks
                .Where(x => x != null)
                .Select(x => x.Result)
                .OrderByDescending(x => x.FMeasure())
                .FirstOrDefault();
        }

        private Quality ProcessPart(TrainItem<ItemInfo>[] trainItems, ThresholdQualityComputer qualityComputer, List<double> thresholds)
        {
            var result = new List<Quality>();
            foreach (double threshold in thresholds)
            {
                var quality = qualityComputer.ComputeQuality(trainItems, threshold);
                result.Add(quality);
            }

            return result
                .OrderByDescending(x => x.FMeasure())
                .FirstOrDefault();
        }
    }
}
